package view.base;
/*
 * Copyright (c) 2003, the JUNG Project and the Regents of the University of
 * California All rights reserved.
 * 
 * This software is open-source under the BSD license; see either "license.txt"
 * or http://jung.sourceforge.net/license.txt for a description.
 * 
 */

import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.algorithms.layout.PolarPoint;
import edu.uci.ics.jung.algorithms.layout.RadialTreeLayout;
import edu.uci.ics.jung.algorithms.layout.TreeLayout;
import edu.uci.ics.jung.graph.DelegateTree;
import edu.uci.ics.jung.graph.DirectedGraph;
import edu.uci.ics.jung.graph.DirectedSparseMultigraph;
import edu.uci.ics.jung.graph.Forest;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.Tree;
import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
import edu.uci.ics.jung.visualization.Layer;
import edu.uci.ics.jung.visualization.VisualizationServer;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
import edu.uci.ics.jung.visualization.control.ScalingControl;
import edu.uci.ics.jung.visualization.decorators.EdgeShape;
import edu.uci.ics.jung.visualization.decorators.EllipseVertexShapeTransformer;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import edu.uci.ics.jung.visualization.subLayout.TreeCollapser;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import model.Formula;
import model.Operator;
import model.Prover;
import model.exceptions.FormulaException;
import org.apache.commons.collections15.Factory;
import org.apache.commons.collections15.Transformer;
import org.apache.commons.collections15.functors.ConstantTransformer;

/**
 * Demonstrates "collapsing"/"expanding" of a tree's subtrees.
 *
 * @author Tom Nelson
 *
 */
@SuppressWarnings("serial")
public class TreeCollapsePanel extends JPanel {

    LinkedList<Operator> operators;
    
    public Prover p;
    public TabsPanel frame;
    public int count = 0;
    public final static Logger EXCEPTIONLOGGER = Logger.getLogger("exception logger");
    public final static Logger FORMULALOGGER = Logger.getLogger("formula logger");
    /**
     * the graph
     */
    boolean proven = false;
    Tree<String, Integer> graph;
    Factory<DirectedGraph<String, Integer>> graphFactory =
            new Factory<DirectedGraph<String, Integer>>() {
                @Override
                public DirectedGraph<String, Integer> create() {
                    return new DirectedSparseMultigraph<String, Integer>();
                }
            };
    Factory<Tree<String, Integer>> treeFactory =
            new Factory<Tree<String, Integer>>() {
                @Override
                public Tree<String, Integer> create() {
                    return new DelegateTree<String, Integer>(graphFactory);
                }
            };
    Factory<Integer> edgeFactory = new Factory<Integer>() {
        int i = 0;

        @Override
        public Integer create() {
            return i++;
        }
    };
    Factory<String> vertexFactory = new Factory<String>() {
        int i = 0;

        @Override
        public String create() {
            return "V" + i++;
        }
    };

    /**
     * cnetruje widok na Roota
     */
    public void centerToRoot() {
        Point2D center = vv.getCenter();
        Point2D rootLocation = layout.transform(graph.getRoot());
        vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT)
                .translate(center.getX() - rootLocation.getX(), (center.getY() - rootLocation.getY()) / 4);
    }
    /**
     * the visual component and renderer for the graph
     */
    VisualizationViewer<String, Integer> vv;
    VisualizationServer.Paintable rings;
    String root;
    TreeLayout<String, Integer> layout;
    @SuppressWarnings("unchecked")
    FRLayout layout1;
    TreeCollapser collapser;
    RadialTreeLayout<String, Integer> radialLayout;

    @SuppressWarnings("unchecked")
    /**
     * creates and prints tree
     *
     * @param formula - formula to prove
     * @param options - proving options
     * @param labels - labeling options
     * @param opfile - file with operators
     * @throws Exception
     */
    public TreeCollapsePanel(TabsPanel f, String formula, int options, int labels, LinkedList<Operator> operators) throws Exception {
        this.setLayout(new BorderLayout());

        this.frame = f;
        // create a simple graph for the demo
        graph = new DelegateTree<String, Integer>();
        collapser = new TreeCollapser();

        this.operators = operators;
        //get formula and options and create the tree
        String logOp=Operator.logOperators(operators);
        String logMes="formula: "+formula+"\n operators "+logOp;
        FORMULALOGGER.fine(logMes);
        proven=modelProverView(formula, operators,options, labels);

        radialLayout = new RadialTreeLayout<String, Integer>(graph);
        radialLayout.setSize(new Dimension(1000,400));

        layout = new TreeLayout<String, Integer>(graph);
         vv = new VisualizationViewer<String, Integer>(layout, new Dimension(1000,400));
        vv.setBackground(Color.white);
        vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line());
        vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
        vv.getRenderContext().setVertexShapeTransformer(new ClusterVertexShapeFunction());
        // add a listener for ToolTips
        vv.setVertexToolTipTransformer(new ToStringLabeller());
        vv.getRenderContext().setArrowFillPaintTransformer(new ConstantTransformer(Color.lightGray));
        rings = new Rings();

        centerToRoot();

        //kolorowanie wierzchołków
        vv.getRenderContext().setVertexFillPaintTransformer(new VertexFill());

        final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv);
        this.add(panel, BorderLayout.CENTER);

        final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse();

        vv.setGraphMouse(graphMouse);

        JComboBox modeBox = graphMouse.getModeComboBox();
        modeBox.addItemListener(graphMouse.getModeListener());
        graphMouse.setMode(ModalGraphMouse.Mode.PICKING);

        final ScalingControl scaler = new CrossoverScalingControl();

        JButton plus = new JButton("+");
        plus.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                scaler.scale(vv, 1.1f, vv.getCenter());
            }
        });
        JButton minus = new JButton("-");
        minus.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                scaler.scale(vv, 1 / 1.1f, vv.getCenter());
            }
        });

        JToggleButton radial = new JToggleButton("Radial");
        radial.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                if (e.getStateChange() == ItemEvent.SELECTED) {
//layout.setRadial(true);
                    vv.setGraphLayout(radialLayout);
                    vv.getRenderContext().getMultiLayerTransformer().setToIdentity();
                    vv.addPreRenderPaintable(rings);
                } else {
//layout.setRadial(false);
                    vv.setGraphLayout(layout);
                    vv.getRenderContext().getMultiLayerTransformer().setToIdentity();
                    vv.removePreRenderPaintable(rings);
                    
                    centerToRoot();
                }

                vv.repaint();
            }
        });

        JButton collapse = new JButton("Collapse");
        collapse.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {

                Collection picked = new HashSet(vv.getPickedVertexState().getPicked());
                if (picked.size() == 1) {
                    Object root = picked.iterator().next();
                    Forest inGraph = (Forest) layout.getGraph();


                    try {
                        collapser.collapse(vv.getGraphLayout(), inGraph, root);
                    } catch (InstantiationException e1) {
                        e1.printStackTrace();
                    } catch (IllegalAccessException e1) {
                        e1.printStackTrace();
                    }

                    vv.getPickedVertexState().clear();
                    vv.repaint();

                }
            }
        });

        JButton expand = new JButton("Expand");
        expand.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {

                Collection picked = vv.getPickedVertexState().getPicked();
                for (Object v : picked) {
                    if (v instanceof Forest) {
                        Forest inGraph = (Forest) layout.getGraph();
                        collapser.expand(inGraph, (Forest) v);
                    }

                    vv.getPickedVertexState().clear();
                    //  vv.getRenderContext().setVertexFillPaintTransformer(vertexColor);
                    vv.repaint();


                }
            }
        });

        JPanel scaleGrid = new JPanel();//new JPanel(new GridLayout(1, 0));
        //scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom"));

        JLabel pl = new JLabel();
        String pt;
        if (proven == true) {
            pt = "OPEN";
            pl.setForeground(new java.awt.Color(0, 153, 0));
        } else {
            pt = "CLOSED";
            pl.setForeground(new java.awt.Color(204, 0, 0));
        }
        pt = "TREE IS " + pt;

        pl.setText(pt);

        String nn = ", NUMBER OF NODES IN THE TREE: " + this.count;
        JLabel pp = new JLabel();
        pp.setText(nn);

        JButton save = new JButton("Save tree in file");
        save.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                SaveTreeInFile();
            }
        });

        JButton save2 = new JButton("Save tree in text file");
        save2.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                SaveInTextFile();
            }
        });
        
        scaleGrid.add(plus);
        scaleGrid.add(minus);

        JPanel important = new JPanel();
        important.add(pl);
        important.add(pp);
        important.add(save);
        important.add(save2);
        this.add(important, BorderLayout.NORTH);
        
        JPanel controls = new JPanel();
        controls.add(radial);
        controls.add(plus);
        controls.add(minus);
        controls.add(modeBox);
        controls.add(collapse);
        controls.add(expand);
        this.add(controls, BorderLayout.SOUTH);
        
    }

    class Rings implements VisualizationServer.Paintable {

        Collection<Double> depths;

        public Rings() {
            depths = getDepths();
        }

        private Collection<Double> getDepths() {
            Set<Double> depths = new HashSet<Double>();
            Map<String, PolarPoint> polarLocations = radialLayout.getPolarLocations();
            for (String v : graph.getVertices()) {
                PolarPoint pp = polarLocations.get(v);
                depths.add(pp.getRadius());
            }
            return depths;
        }

        @Override
        public void paint(Graphics g) {
            g.setColor(Color.lightGray);

            Graphics2D g2d = (Graphics2D) g;
            Point2D center = radialLayout.getCenter();

            Ellipse2D ellipse = new Ellipse2D.Double();
            for (double d : depths) {
                ellipse.setFrameFromDiagonal(center.getX() - d, center.getY() - d,
                        center.getX() + d, center.getY() + d);
                Shape shape = vv.getRenderContext().
                        getMultiLayerTransformer().getTransformer(Layer.LAYOUT).transform(ellipse);
                g2d.draw(shape);
            }
        }

        @Override
        public boolean useTransform() {
            return true;
        }
    }

    /**
     * a demo class that will create a vertex shape that is either a polygon or
     * star. The number of sides corresponds to the number of vertices that were
     * collapsed into the vertex represented by this shape.
     *
     * @author Tom Nelson
     *
     * @param <V>
     */
    class ClusterVertexShapeFunction<V> extends EllipseVertexShapeTransformer<V> {

        ClusterVertexShapeFunction() {
            setSizeTransformer(new ClusterVertexSizeFunction<V>(20));
        }

        @SuppressWarnings("unchecked")
        @Override
        public Shape transform(V v) {
            if (v instanceof Graph) {
                int size = ((Graph) v).getVertexCount();
                if (size < 8) {
                    int sides = Math.max(size, 3);
                    return factory.getRegularPolygon(v, sides);
                } else {
                    return factory.getRegularStar(v, size);
                }
            }
            return super.transform(v);
        }
    }

    /**
     * A demo class that will make vertices larger if they represent a collapsed
     * collection of original vertices
     *
     * @author Tom Nelson
     *
     * @param <V>
     */
    class ClusterVertexSizeFunction<V> implements Transformer<V, Integer> {

        int size;

        public ClusterVertexSizeFunction(Integer size) {
            this.size = size;
        }

        @Override
        public Integer transform(V v) {
            if (v instanceof Graph) {
                return 30;
            }
            return size;
        }
    }

    private boolean modelProverView(String formula, LinkedList<Operator> operators, int options, int labels)
            throws Exception {


        boolean prove = false, state;

        p = new Prover(formula, operators, options);
        try {
            prove = p.prove();
        } catch (Exception e) {
            throw e;
        }
        //czyszczenie śmieci - program RAM usage friendly
        Runtime.getRuntime().gc();
        
        DelegateTree<Formula, Integer> Tree = p.getTree();
        count = Tree.getVertexCount();
        paintTree(Tree, operators, labels);
        return prove;
    }

    private void paintTree(DelegateTree<Formula, Integer> tree, LinkedList<Operator> operators, int labels) {
        paintCurr(tree, tree.getRoot(), operators, labels);
    }

    private void paintCurr(DelegateTree<Formula, Integer> tree, Formula current, LinkedList<Operator> operators, int labels) {
        String Scurrent = current.printMe(labels);
        if (tree.isRoot(current)) {

            //System.out.println(Scurrent);
            graph.addVertex(Scurrent);
        }

        //System.out.println("current " + Scurrent);

        Collection<Formula> Children = tree.getChildren(current);

        int size = Children.size();
        String kid;
        Object[] child = Children.toArray();
        Formula oneKid;

        for (int i = 0; i < size; i++) {
            oneKid = (Formula) child[i];
            //co wypisuje się na dziecku
            kid = oneKid.printMe(labels);
            //System.out.println("dodaje dzieci do " +Scurrent+" :"+kid);
            graph.addEdge(edgeFactory.create(), Scurrent, kid);
            //jeśli ma dzieci
            if (tree.isLeaf(oneKid) == false) {
                paintCurr(tree, oneKid, operators, labels);
            }

        }
    }

    public void SaveTreeInFile() {
        this.frame.saveTreeXML(this);
    }

    public void SaveInTextFile() {
        this.frame.saveTreeTxt(this);
    }
}